﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace IRC.NET
{
    public class ServerReply
    {
        #region Reply Codes
        public const int RPL_WELCOME = 001;
        public const int RPL_YOURHOST = 002;
        public const int RPL_CREATED = 003;
        public const int RPL_MYINFO = 004;
        public const int RPL_BOUND = 005;
        public const int RPL_USERHOST = 302;
        public const int RPL_ISON = 303;
        public const int RPL_AWAY = 301;
        public const int RPL_UNAWAY = 305;
        public const int RPL_NOWAWAY = 306;
        public const int RPL_WHOISUSER = 311;
        public const int RPL_WHOISSERVER = 312;
        public const int RPL_WHOISOPERATOR = 313;
        public const int RPL_WHOISIDLE = 317;
        public const int RPL_ENDOFWHOIS = 318;
        public const int RPL_WWHOISCHANNELS = 319;
        public const int RPL_WHOWASUSER = 314;
        public const int RPL_ENDOFWHOWAS = 369;
        public const int RPL_LISTSTART = 321;
        public const int RPL_LIST = 322;
        public const int RPL_LISTEND = 323;
        public const int RPL_UNIQOPIS = 325;
        public const int RPL_CHANNELMODEIS = 324;
        public const int RPL_NOTOPIC = 332;
        public const int RPL_TOPIC = 332;
        public const int RPL_INVITING = 341;
        public const int RPL_SUMMONING = 342;
        public const int RPL_INVITELIST = 346;
        public const int RPL_ENDOFINVITELIST = 347;
        public const int RPL_EXCEPTLIST = 348;
        public const int RPL_ENDOFEXCEPTLIST = 349;
        public const int RPL_VERSION = 351;
        public const int RPL_WHOREPLY = 352;
        public const int RPL_ENDOFWHO = 315;
        public const int RPL_NAMREPLY = 353;
        public const int RPL_ENDOFNAMES = 366;
        public const int RPL_LINKS = 364;
        public const int RPL_ENDOFLINKS = 365;
        public const int RPL_BANLIST = 367;
        public const int RPL_ENDOFBANLIST = 368;
        public const int RPL_INFO = 371;
        public const int RPL_ENDOFINFO = 374;
        public const int RPL_MOTDSTART = 375;
        public const int RPL_MOTD = 372;
        public const int RPL_ENDOFMOTD = 376;
        public const int RPL_YOUREOPER = 381;
        public const int RPL_REHASHING = 382;
        public const int RPL_YOURESERVICE = 383;
        public const int RPL_TIME = 391;
        public const int RPL_USERSSTART = 393;
        public const int RPL_USERS = 393;
        public const int RPL_ENDOFUSERS = 394;
        public const int RPL_NOUSERS = 395;
        public const int RPL_TRACELINK = 200;
        public const int RPL_TRACECONNECTING = 201;
        public const int RPL_TRACEHANDSHAKE = 202;
        public const int RPL_TRACEUNKNOWN = 203;
        public const int RPL_TRACEOPERATOR = 204;
        public const int RPL_TRACEUSER = 205;
        public const int RPL_TRACESERVER = 205;
        public const int RPL_TRACESERVICE = 207;
        public const int RPL_TRACENEWTYPE = 208;
        public const int RPL_TRACECLASS = 209;
        public const int RPL_TRACERECONNECT = 210;
        public const int RPL_TRACELOG = 261;
        public const int RPL_TRACEEND = 262;
        public const int RPL_STATSLINKINFO = 211;
        public const int RPL_STATSCOMMANDS = 212;
        public const int RPL_ENDOFSTATS = 219;
        public const int RPL_STATSUPTIME = 242;
        public const int RPL_STATSOLINE = 243;
        public const int RPL_UMODEIS = 221;
        public const int RPL_SERVLIST = 234;
        public const int RPL_SERVLISTEND = 235;
        public const int RPL_LUSERCLIENT = 251;
        public const int RPL_LUSEROP = 252;
        public const int RPL_LUSERUNKNOWN = 253;
        public const int RPL_LUSERCHANNELS = 254;
        public const int RPL_LUSERME = 255;
        public const int RPL_ADMINME = 256;
        public const int RPL_ADMINLOC1 = 257;
        public const int RPL_ADMINLOC2 = 258;
        public const int RPL_ADMINEMAIL = 259;
        public const int RPL_TRYADAMIN = 263;

        public const int RPL_NICKTIME = 333;
        public const int ERR_LINKCHANNEL = 470;
        public const int RPL_FORWARD = 379;
        #endregion

		private static string MESSAGE_PATTERN = @"^(:(?<server>[\S]+)\s+)?((?<opcode>\d+)\s+)?(?<username>[\S]+)\s*((?<message>.*))?";

        public int MessageCode
        {
            get;
            set;
        }

        public string Prefix
        {
            get;
            set;
        }

        public string Username
        {
            get;
            set;
        }

        public string Message
        {
            get;
            set;
        }

        public ServerReply()
        {
            this.Message = string.Empty;
            this.Prefix = string.Empty;
            this.Username = string.Empty;
            this.MessageCode = -1;
        }

        public ServerReply(string message)
        {
            ParseMessage(message);
        }

        public void ParseMessage(string message)
        {
            if (string.IsNullOrEmpty(message))
            {
                throw new ArgumentNullException("Cannot parse an empty message.");
            }
            else if ((message[0] != ':') || (char.IsDigit(message[0])))
            {
                throw new ArgumentException("Invalid format. Could not find prefix delimeter or message code.");
            }

            try
            {
                if (Regex.IsMatch(message, MESSAGE_PATTERN))
                {
                	Match match = Regex.Match(message, MESSAGE_PATTERN);

                	this.Prefix = match.Groups["server"].Value;
                	this.Message = match.Groups["message"].Value;
                	this.Username = match.Groups["username"].Value;

					string messageCode = match.Groups["opcode"].Value;
					this.MessageCode = !string.IsNullOrEmpty(messageCode) ? int.Parse(messageCode) : -1;
                }
                else
                {
                    throw new FormatException(string.Format("Message did not match expected format: {0}", message));
                }
            }
            catch (Exception e)
            {
                throw new ArgumentException("Cannot parse this message. Please check format.", e);
            }
        }

        public static ServerReply Parse(string message)
        {
            ServerReply serverMessage = new ServerReply();
            serverMessage.ParseMessage(message);

            return (serverMessage);
        }
    }
}
